# encoding: utf-8
import time, json, pexpect, os, requests

from app.extensions import log, db
from flask_restplus import Resource, fields
from app.extensions.api import Namespace, abort, http_exceptions
from app.modules.common.utils import DbCommit, checkToken, ComplexEncoder, JsonFileUtil
from app.modules.common.schemas import BaseSchema
from . import parameters, schemas
from app.modules.common.parameters import BaseParameters
from app.modules.agent.models import Agent
from app.modules.loadLevel.models import LoadLevel
from app.modules.machine.models import Machine
from app.modules.apply.models import ApplyRecord
from password import Pass
from app.modules.operation.resources import addOperationRecord
from app.modules.common.dbUtil import createId
from flask_restplus_patched import Parameters

api = Namespace('agent', description = "ucloud-agent")  # pylint: disable=invalid-name


@api.route('/')
class AgentList(Resource):
    @api.parameters(parameters.AgentListParameters())
    @api.response(schemas.AgentListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        查询ucloud-agent信息列表
        :param args:
        :return:
        """
        log.info("============== start query ucloud-agent list ==============")
        _agent_id = args['agent_id']
        _is_only_free = args['is_only_free']
        log.info("============== agent_id is %s ==============" % _agent_id)
        log.info("============== is_only_free is %s ==============" % _is_only_free)
        if _agent_id is None:
            # _list = db.session.query(Agent, ApplyRecord).join(ApplyRecord, Agent.ip == ApplyRecord.agent_ip,
            #                                                   isouter = True).all()
            _result = db.session.execute(
                "select * from agent left join apply_record on agent.ip = apply_record.agent_ip where agent.is_del='1'")
        else:
            # _list = db.session.query(Agent, ApplyRecord).filter(Agent.ip == ApplyRecord.agent_ip,
            #                                                     Agent.id == _agent_id).all()
            _result = db.session.execute(
                "select * from agent left join apply_record on agent.ip = apply_record.agent_ip WHERE agent.id = :id and agent.is_del='1'",
                {'id': _agent_id})

        _list = []
        for row in _result:
            _r = json.loads(json.dumps(dict(row), encoding = "UTF-8", ensure_ascii = False, cls = ComplexEncoder))
            if _is_only_free is not None and _is_only_free:
                if row.record_id is None:
                    _list.append(_r)
            else:
                _list.append(_r)
        return {'success': True, 'msg': '', 'data': _list}


@api.route('/update')
class AgentUpdate(Resource):
    @api.parameters(parameters.AgentUpdateParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        修改ucloud-agent信息
        :param args:
        :return:
        """
        log.info("============== start update ucloud-agent info ==============")
        _agent_id = args['agent_id']
        # _record_id = args['record_id']
        _machine_ip = args['machine_ip']
        _load_level = args['load_level']
        _vm_id = args['vm_id']
        # _expect_recover_date = args['expect_recover_date']
        log.info("============== agent_id is %s ==============" % _agent_id)
        # log.info("============== record_id is %s ==============" % _record_id)
        log.info("============== machine_ip is %s ==============" % _machine_ip)
        log.info("============== load_level is %s ==============" % _load_level)
        # log.info("============== expect_recover_date is %s ==============" % _expect_recover_date)
        # if _record_id is not None and _expect_recover_date is None:
        #     _msg = 'record_id存在时 _expect_recover_date不能为空'
        #     log.error('============== %s ==============' % _msg)
        #     return {'success': False, 'msg': _msg}

        _agent = Agent.query.filter_by(id = _agent_id).filter_by(is_del = '1').first()
        if _agent is None:
            _msg = 'agent不存在 agent_id:%s' % _agent_id
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}

        _level = LoadLevel.query.filter_by(id = _load_level).first()
        if _level is None:
            _msg = '负载级别不存在 load_level:%s' % _load_level
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}

        _machine = Machine.query.filter_by(ip = _machine_ip).first()
        if _machine is None:
            _msg = '实体机不存在 machine_ip:%s' % _machine_ip
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}

        # if _record_id is not None:
        #     _apply = ApplyRecord.query.filter_by(record_id = _record_id).first()
        #     if _apply is None:
        #         _msg = '申请记录不存在 record_id:%s' % _record_id
        #         log.error('============== %s ==============' % _msg)
        #         return {'success': False, 'msg': _msg}
        #     else:
        #         _apply.expect_recover_date = _expect_recover_date
        addOperationRecord('UPDATE_AGENT', 'machine_ip', _agent.machine_ip, _machine_ip, 'update ucloud-agent',
                           **kwargs)
        addOperationRecord('UPDATE_AGENT', 'load_level', _agent.load_level, _load_level, 'update ucloud-agent',
                           **kwargs)
        _agent.machine_ip = _machine_ip
        _agent.load_level = _load_level
        _agent.level_name = _level.level_name
        _agent.vm_id = _vm_id

        return DbCommit()


@api.route('/st')
class AgentSt(Resource):
    @api.parameters(parameters.AgentStParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        远程启停agent应用 根据agent状态自动启动/停止
        :param args:
        :return:
        """
        log.info("============== start st ucloud-agent app ==============")
        _agent_id = args['agent_id']
        log.info("============== agent_id is %s ==============" % _agent_id)
        _agent = Agent.query.filter_by(id = _agent_id).filter_by(is_del = '1').first()
        if _agent is None:
            _msg = 'agent不存在 agent_id:%s' % _agent_id
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}
        else:
            """
            远程调用agent机器脚本执行
            """
            addOperationRecord('ST_AGENT', _agent.ip, None, None, 'st ucloud-agent', **kwargs)
            _cmd = 'rt silent'
            return _runShellOnAgent(_cmd, _agent.ip)


@api.route('/del')
class AgentDel(Resource):
    @api.parameters(parameters.AgentStParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        删除agent
        :param args:
        :return:
        """
        log.info("============== start del ucloud-agent ==============")
        _agent_id = args['agent_id']
        log.info("============== agent_id is %s ==============" % _agent_id)

        _agent = Agent.query.filter_by(id = _agent_id).filter_by(is_del = '1').first()
        if _agent is None:
            _msg = 'agent不存在 agent_id:%s' % _agent_id
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}
        else:
            _apply = ApplyRecord.query.filter_by(agent_ip = _agent.ip).first()
            if _apply is not None:
                _msg = 'agent正在使用中,不能删除 agent_id:%s . record_id:%s ' % (_agent_id, _apply.record_id)
                log.error('============== %s ==============' % _msg)
                return {'success': False, 'msg': _msg}
            else:
                addOperationRecord('DEL_AGENT', _agent.ip, None, None, 'del ucloud-agent', **kwargs)
                # 删除改为逻辑删
                _agent.is_del = '0'
                # db.session.delete(_agent)
                return DbCommit()


@api.route('/init')
class AgentInit(Resource):
    @api.parameters(parameters.AgentStParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        初始化/升级agent
        :param args:
        :return:
        """
        log.info("============== start init ucloud-agent ==============")
        _agent_id = args['agent_id']
        log.info("============== agent_id is %s ==============" % _agent_id)

        _agent = Agent.query.filter_by(id = _agent_id).filter_by(is_del = '1').first()
        if _agent is None:
            _msg = 'agent不存在 agent_id:%s' % _agent_id
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}
        else:
            """
            远程调用agent机器脚本执行
            """
            addOperationRecord('INIT_AGENT', _agent.ip, None, None, 'init ucloud-agent', **kwargs)
            _cmd = 'cd /usr/local/src/beetle-vm-init/ && svn revert -R ./ && svn update && chmod 777 -R * && update-ucloud.sh silent'
            return _runShellOnAgent(_cmd, _agent.ip)


@api.route('/add')
class AgentAdd(Resource):
    @api.parameters(parameters.AgentAddParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        新增agent
        :param args:
        :return:
        """
        log.info("============== start add ucloud-agent ==============")
        _ip = args['ip']
        _load_level = args['load_level']
        _machine_ip = args['machine_ip']
        _vm_id = args['vm_id']
        log.info("============== ip is %s ==============" % _ip)
        log.info("============== load_level is %s ==============" % _load_level)
        log.info("============== machine_ip is %s ==============" % _machine_ip)
        log.info("============== vm_id is %s ==============" % _vm_id)

        _level = LoadLevel.query.filter_by(id = _load_level).first()
        if _level is None:
            _msg = '负载级别不存在 load_level:%s' % _load_level
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}

        _machine = Machine.query.filter_by(ip = _machine_ip).first()
        if _machine is None:
            _msg = '实体机不存在 machine_ip:%s' % _machine_ip
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}

        _agent1 = Agent.query.filter_by(ip = _ip).filter_by(is_del = '1').first()
        if _agent1 is not None:
            _msg = '虚拟机 %s 已存在' % _ip
            log.error('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}

        addOperationRecord('ADD_AGENT', _ip, None, None, 'add ucloud-agent', **kwargs)
        from app.modules.common.dbUtil import createId
        _agent = Agent(createId('ag'), _ip, _load_level, _machine_ip, _level.level_name, vm_id = _vm_id)
        db.session.add(_agent)
        return DbCommit()


@api.route('/getZoneList')
class GetZoneList(Resource):
    @api.parameters(BaseParameters())
    @api.response(schemas.ZoneListSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        获取区域列表
        :param args:
        :return:
        """
        log.info("============== start get zone list ==============")
        _list = [
            {
                "zone_code": "uc.gzidc.area",
                "zone_title": "中国区",
                "parent_code": None
            },
            {
                "zone_code": "uc.gzidc.warehouse.szx",
                "zone_title": "深圳仓",
                "parent_code": "uc.gzidc.area"
            },
            {
                "zone_code": "uc.gzidc.warehouse.can",
                "zone_title": "广州仓",
                "parent_code": "uc.gzidc.area"
            },
            {
                "zone_code": "uc.usaws.area",
                "zone_title": "美国区",
                "parent_code": None
            },
            {
                "zone_code": "uc.usaws.warehouse.usla",
                "zone_title": "美西仓",
                "parent_code": "uc.usaws.area"
            },
            {
                "zone_code": "uc.usaws.warehouse.usky",
                "zone_title": "美东仓",
                "parent_code": "uc.usaws.area"
            },
            {
                "zone_code": "uc.euaws.area",
                "zone_title": "欧洲区",
                "parent_code": None
            },
            {
                "zone_code": "uc.euaws.warehouse.bemo",
                "zone_title": "利时仓",
                "parent_code": "uc.euaws.area"
            },
            {
                "zone_code": "uc.euaws.warehouse.debre",
                "zone_title": "德国仓",
                "parent_code": "uc.euaws.area"
            },
            {
                "zone_code": "uc.euaws.warehouse.ukle",
                "zone_title": "英国一仓",
                "parent_code": "uc.euaws.area"
            },
            {
                "zone_code": "uc.euaws.warehouse.ukma",
                "zone_title": "英国二仓",
                "parent_code": "uc.euaws.area"
            },
            {
                "zone_code": "uc.auaws.area",
                "zone_title": "澳洲区",
                "parent_code": None
            },
            {
                "zone_code": "uc.auaws.warehouse.ausyd",
                "zone_title": "澳洲仓",
                "parent_code": "uc.auaws.area"
            },
            {
                "zone_code": "uc.gzidc.warehouse.ngb",
                "zone_title": "宁波仓",
                "parent_code": "uc.gzidc.area"
            },
            {
                "zone_code": "uc.gzidc.warehouse.tao",
                "zone_title": "青岛仓",
                "parent_code": "uc.gzidc.area"
            },
            {
                "zone_code": "uc.gzidc.warehouse.hkg",
                "zone_title": "香港仓",
                "parent_code": "uc.gzidc.area"
            },
            {
                "zone_code": "uc.gzidc.warehouse.sha",
                "zone_title": "上海仓",
                "parent_code": "uc.gzidc.area"
            }
        ]
        return {'success': True, 'msg': '', 'data': _list}


def _runShellOnAgent(_cmd, _ip):
    from config import BaseConfig
    shell_file = os.path.join(BaseConfig.PROJECT_ROOT, 'shell/op_agent.sh')
    _sh = 'bash %s %s %s %s "%s"' % (shell_file, BaseConfig.PROJECT_ROOT, Pass.AGENT_ROOT_PASS, _ip, _cmd)
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
    if status == 0:
        _msg = 'init ucloud-agent success.'
        log.info('============== %s ==============' % _msg)
        return {'success': True, 'msg': _msg}
    else:
        _msg = 'init ucloud-agent failure, cause: %s.' % output
        log.info('============== %s ==============' % _msg)
        return {'success': False, 'msg': _msg}


@api.route('/importAgent')
class ImportAgent(Resource):
    @api.parameters(BaseParameters())
    @api.response(BaseSchema(many = False))
    @checkToken
    def get(self, args, **kwargs):
        """
        初始导入本地虚拟机列表
        :param args:
        :return:
        """
        log.info("============== start import agent ==============")
        _path = "/usr/local/Beetle/uCloud"
        _dir = os.listdir(_path)
        _agents = []
        for file in _dir:
            _agents.append(JsonFileUtil.load('%s/%s' % (_path, file)))
        if len(_agents) == 0:
            _msg = '本地agent列表为空'
            log.info('============== %s ==============' % _msg)
            return {'success': False, 'msg': _msg}

        _machine = Machine.query.filter_by(ip = '172.16.1.1').first()
        if _machine is None:
            db.session.add(Machine('172.16.1.1', 1, 1, 1))
            db.session.commit()

        _ips = []

        for _a in _agents:
            if str(_a['ip']) not in _ips and Agent.query.filter_by(ip = _a['ip']).filter_by(
                    is_del = '1').first() is None:
                _agent = Agent(createId('ag'), str(_a['ip']), 1, '172.16.1.1', 'L')
                db.session.add(_agent)
                _ips.append(str(_a['ip']))
        db.session.commit()

        return {'success': True, 'msg': ''}


@api.route('/sync')
class Sync(Resource):
    @api.parameters(Parameters())
    @api.response(BaseSchema(many = False))
    def get(self, args, **kwargs):
        """
        刷新所有agent的zone和app信息
        :param args:
        :return:
        """
        log.info("============== start sync agent ==============")
        _rtn = syncZoneAndApps()
        return {'success': True, 'msg': _rtn}


def syncZoneAndApps():
    """
    调用所有agent的接口，执行同步
    """
    _rtn = ''
    for _agent in Agent.query.filter_by(is_del = '1').all():
        _url = 'http://%s:5000/api/v1/zones/sync' % _agent.ip
        try:
            _obj = requests.get(url = _url, timeout = 20).json()
            log.info('request %s for sync . rtn is %s' % (_agent.ip, _obj))
            _rtn = '%s/n%s:%s' % (_rtn, _agent.ip, _obj)
        except BaseException as e:
            log.info('request %s for sync is error. %s' % (_agent.ip, e.message))
            _rtn = '%s%s:is error . %s/n/r' % (_rtn, _agent.ip, e.message)
    return _rtn
